--[[
The purpose of this module is to allow data to be safely passed between one
player's mod and another - to serve as a replication/synchronization mechanism.
For this reason, one player is always indicated as the "master" of data for
a particular raid.  If this player sends out sync data, all clients should
verify that their data matches it.  If another player sends out sync data,
then it should only be overwritten if it's from a 'closer' source.
Distance is measured as
'0' if from the master himself,
'1' from someone that got it directly from the master or was in the raid itself
X+1 if this data was received from anyone else where X is the source's distance

Probably should display a warning if someone with the same distance reports
conflicting information

Note that Sync data has a separate addon channel prefix from normal raid traffic.

There are two main types of methods in this file
SendSyncMessage - these methods trigger from actions caught by this mod.
   Often these are only triggered by the master
Verify?? - these are actually stored in each module's file, but are triggered by
	SNFParseSyncMessage, which attempts to understand and direct all
			 incoming sync messages

Still not figured out:
----------------------
How to space out updates (isn't there an update event I can hook into?)
Probably need a queue to hold known gaps that will then be worked as time allows
For example, if a raid leader posts Auction 20, but my last auction for a raid was
only #17, then I'd want to queue up a request for auction #18 and #19 so that
my gaps are filled in.  Or if I hear someone mention a RAIDID that I don't have,
I should queue up a request to load that entire set of data.

]]--

--make these local eventually - global for saving for debugging for now
parseq = { }
msgq = { }


function QueueMessage(msg)
	if not msgq then msgq = { } end
	table.insert(msgq, msg)
end

function SNFCheckCanInitiateSync()
	-- During a raid, you probably only want one mod initiating/controlling syncs.
	-- this will hold the logic to determine if I'm that one mod.
	return UnitName("player") == "Farmer" or UnitName("player") == "Lascha"
end

function SNFParseSyncMessage(msg, sender)


	-- look for auto sync messages first, they don't have the same format as other messages (no leading Raidid)
	local lEvent, rest = strmatch(msg, "(.-)|(.*)")

	if lEvent and rest and lEvent == "SYNC_AUTO" then
		HandleSyncAuto(rest, sender)
		return
	end
end

--[[
	Item will indicate the variable being used.
]]


function SNFAutoSync(--[[version,]] item, path)

--Message looks like:  "SYNC_AUTO|v1|Raids|testRaid~sub~sub2|setme=true"

	SNFDEBUG(4, "SNFAutoSync("..(item or "nil")..","..(path or "nil"))
	local root = nil
	local version = 1

	if not msgq then msgq = { } end
	if not parseq then parseq = { } end

	if item == "Raids" then		root = SNFRaids		end
	if item == "Auctions" then	root = SNFAuctions	end

	if not root then
		SNFDEBUG(3, "Root not found from item "..(item or "nil")..". Autosync send failed")
	end

	-- navigate down to the point indicated by "path"
	local sofar = path
	local actual = nil
	while sofar do
		local nxt, rest = strmatch(sofar,"(.-)~(.*)")

		if not nxt then
			nxt = sofar
			rest = nil
		end
		-- had a problem with numeric indexes being treated as a string - and therefore indexed data not being found
		-- maybe this will fix it
		if nxt and tonumber(nxt) then
			nxt = tonumber(nxt)
		end
		SNFDEBUG(3, "Separated "..(nxt or "nil").." from "..(rest or "nil"))
		if not root[nxt] and type(root[nxt]) == "nil" then
--			SNFPRINT(3, "Root", root)
			SNFPRINT(3, "root[nxt]", root[nxt])
			SNFPRINT(3, "root[1]", root[1])
			SNFDEBUG(1, "Can't find node at "..(actual or "nil").." called "..(nxt or "nil").." skipping branch")
			SNFDEBUG(3, "sofar is "..(sofar or "nil").." rest is "..(rest or "nil").." nxt is "..(nxt or "nil").." type of nxt is "..type(nxt))
			return
		else
			SNFDEBUG(3, "Navigated down through node: "..(nxt or "nil"))

			if actual then
				actual = actual.."~"
			else
				actual = ""
			end
			actual = actual..nxt
			root = root[nxt]
		end
		sofar = rest
	end
	SNFDEBUG(3, "Actual path is "..(actual or "nil"))
	-- now need to determine what data to send on to the other side.
	local m = "SYNC_AUTO|v"..version.."|"..item.."|"..actual.."|"

	for key, val in pairs(root) do
		if (type(val) == "table") then
			local dd = actual.."~"..key
			SNFDEBUG(3, "Adding "..dd.." to parse queue")
			tinsert(parseq, { item, actual.."~"..key})
		else
			if val then
				local dd = m..key.."="..val
				QueueMessage(dd)
			end
		end
	end

	--SNFDEBUG(2, "Message Queue looks like: ")
--	for i, msg in ipairs(msgq) do
--		SNFDEBUG(2,"MSGQ: "..i..":"..gsub(msg, "\124", "\124\124"))
--	end
	-- ParseQ
--	for i, pair in ipairs(parseq) do
--		if pair and pair[1] and pair[2] then
--			SNFDEBUG(2, "PARSEQ:"..i..":"..pair[1].."->"..pair[2])
--		end
--	end

	if #(msgq) > 0 then
		SNFDEBUG(3, "Messages queued: "..#(msgq))

	end
	if #(parseq) > 0 then
		SNFDEBUG(2, "Parse queue size: "..#(parseq))
	end
end

function HandleSyncAuto(msg, sender)

	if sender == UnitName("player") then return end -- Skip my own messages

	SNFDEBUG(3, "<- "..gsub(msg, "\124", "\124\124"))

	--Message looks like:  "v1|Raids|testRaid~sub~sub2|setme=true"
	local version, item, path, data = strmatch(msg,"v(.-)|(.-)|(.-)|(.*)")

	if not( version and item and path and data) then
		SNFDEBUG(3, "Bad auto sync message from "..sender)
		return
	end

	if tonumber(version) ~= 1 then
		SNFDEBUG(3, "unknown auto sync version from "..sender)
		return
	end

	local root = nil
	if item == "Raids" then		root = SNFRaids		end
	if item == "Auctions" then	root = SNFAuctions	end

	if not root then
		SNFDEBUG(3, "Root not found from item "..(item or "nil")..". Autosync receive failed")
	end

	-- navigate down to the point indicated by "path"
	local sofar = path
	while sofar do
		local nxt, rest = strmatch(sofar,"(.-)~(.*)")
		if not nxt then
			nxt = sofar
			rest = nil
		end
		-- had a problem with numeric indexes being treated as a string - and therefore indexed data not being found
		-- maybe this will fix it
		if nxt and tonumber(nxt) then
			nxt = tonumber(nxt)
		end
		SNFDEBUG(3, "Separated "..(nxt or "nil").." from "..(rest or "nil"))
		if not root[nxt] then
			SNFDEBUG(3, "Creating node: "..(nxt or "nil"))
			root[nxt] = { }
		else
			SNFDEBUG(3, "Navigated down through node: "..(nxt or "nil"))
		end
		root = root[nxt]
		sofar = rest
	end
	-- root should now be the table we need to work with.
	local var, val = strmatch(data, "(.-)=(.*)")

	if not (var and val) then
		SNFDEBUG(3, "Badly formed auto sync message (data) from "..sender)
		return
	end
	SNFDEBUG(3, "Setting "..var.."="..val)
	root[var] = val
end

local msgcount = 0
function SNFCheckSyncMessages()  -- called every once in 100 updates or so

	if not msgq then msgq = { } end
	if #(msgq) == 0 and #(parseq) > 0 then
		local iterate = table.remove(parseq)
		if iterate then
			SNFDEBUG(2, "Iterating on "..iterate[1].." and "..iterate[2])
			SNFAutoSync(iterate[1], iterate[2])
		end
	end
	if #(msgq) > 0 then

		local msg = table.remove(msgq, 1)
		if msg then
			SNFDEBUG(3, "-> "..gsub(msg, "\124", "\124\124"));
			SendAddonMessage(CHAT_PREFIX_SYNC, msg, RAID, nil)
			msgcount = msgcount + 1
			if #(msgq) == 0 then
				SNFDEBUG(2, "Number of messages sent this session: "..msgcount)
			end
		else
			SNFDEBUG(4, "Queue size is "..#(msgq).." but nothing being removed")
		end
	else
		--SNFDEBUG(3, "AutoSyncMessage: Nothing to send.");
	end
end

